package lt.marsrutai.mobile.android.util;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import lt.marsrutai.mobile.android.AppContext;
import lt.marsrutai.mobile.android.MarsrutaiLTApp;
import lt.marsrutai.mobile.android.R;
import lt.marsrutai.mobile.android.adapter.MultiInfoWindowAdapter.CustomInfoWindowAdapter;
import lt.marsrutai.mobile.android.model.VehiclePosition;
import lt.marsrutai.mobile.android.model.enums.DateTime;
import lt.marsrutai.mobile.android.service.ShapeManipulationService;
import lt.marsrutai.mobile.android.service.TransportInfoResolver.TransportInfo;
import lt.marsrutai.mobile.android.task.GetVehiclePositionsTask;
import lt.marsrutai.mobile.android.task.NotifyingAsyncTask.AsynTaskHandler;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.support.v4.content.LocalBroadcastManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.ToggleButton;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

/**
 * <p>Provides means to display real time vehicle positions on a map.</p>
 * <p>Manages vehicle position acquisition task and markers that are displayed on the map.</p> 
 * 
 * @author Justas
 */
public class VehiclePositionManager {
    
    // how often is vehicle position acquisition task executed (ms)
    private static final int TASK_EXECUTION_PERIOD = 10000;
    
    private Timer timer;
    private Map<Marker, VehiclePosition> vehiclePositionMarkers = new HashMap<Marker, VehiclePosition>();
    private Context context;

    private Bitmap backgroundShape;
    private Bitmap backgroundShapePointer;
    
    private ToggleButton vehiclePositionToggler;
    private GetVehiclePositionsTaskCriteriaProvider taskCriteriaProvider;

    public VehiclePositionManager(Context context) {
        this.context = context;
        
        ShapeManipulationService shapeManipulationService = AppContext.getShapeManipulationService();
        backgroundShape = shapeManipulationService.drawableToBitmap(context.getResources().getDrawable(R.drawable.map_vehicle_background_shape));
        backgroundShapePointer = shapeManipulationService.drawableToBitmap(context.getResources().getDrawable(R.drawable.map_vehicle_background_shape_pointer));
    }
    
    /**
     * Draws vehicles on the map
     * 
     * @param vehiclePositions
     * @param scopeFolder
     */
    public void drawVehiclePositions(GoogleMap map, List<VehiclePosition> vehiclePositions) {
        clear();

        if (vehiclePositions != null) {
            for (VehiclePosition vehiclePosition : vehiclePositions) {
                LatLng latLng = new LatLng(vehiclePosition.getLatitude(), vehiclePosition.getLongitude());

                ShapeManipulationService shapeManipulationService = AppContext.getShapeManipulationService();
                Bitmap transportIcon = shapeManipulationService.drawableToBitmap(AppContext.getTransportInfoResolver().resolveTransportIcon(vehiclePosition.getScopeFolder(), vehiclePosition.getTransportId()));
                Bitmap backgroundShape = this.backgroundShape;
                
                // display pointer only when the angle is available
                Double directionAngle = vehiclePosition.getDirectionAngle();
                if (directionAngle != null) {
                    Bitmap rotatedPointerShape = shapeManipulationService.rotateShape(context, backgroundShapePointer, directionAngle.floatValue());
                    backgroundShape = shapeManipulationService.combineShapes(backgroundShape, rotatedPointerShape);
                }
                
                Bitmap combinedTransportMapIcon = shapeManipulationService.combineShapes(backgroundShape, transportIcon);

                MarkerOptions markerOptions = new MarkerOptions()
                    .position(latLng)
                    .icon(BitmapDescriptorFactory.fromBitmap(combinedTransportMapIcon))
                    .anchor(0.5f, 0.5f)
                    .title(vehiclePosition.getDirectionName())
                    .snippet(context.getString(R.string.vehicle_position_last_updated, DateTime.formatHourMinuteSecond(vehiclePosition.getPositionTime())));
                vehiclePositionMarkers.put(map.addMarker(markerOptions), vehiclePosition);
            }
        }
    }

    /**
     * Clears map of vehicles drawn in {@link #drawVehiclePositions(List, String)}.
     */
    public void clear() {
        for (Marker marker : vehiclePositionMarkers.keySet()) {
            marker.remove();
        }
        vehiclePositionMarkers.clear();
    }

    /**
     * Stops updating vehicle positions
     */
    public void cancel() {
        pause();
        
        vehiclePositionToggler.setOnCheckedChangeListener(null);
    }

    /**
     * Pauses updating vehicle positions
     */
    public void pause() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
    
    public void resume() {
        if (vehiclePositionToggler.isChecked()) {
            start();
        }
    }
    
    /**
     * Starts a task to acquire vehicle positions periodically
     * 
     * @param params
     */
    public void start() {
        if (timer != null) {
            return;
        }

        // start only if the task has not already been started
        TimerTask timerTask = new TimerTask() {
            public void run() {
                GetVehiclePositionsTask task = new GetVehiclePositionsTask();
                task.setCallback(new AsynTaskHandler<Void>() {

                    @Override
                    public void onProgressUpdate(Void... progress) {
                    }

                    @Override
                    public void onCancelled(Intent resultIntent) {
                    }

                    @Override
                    public void onFinished(Intent result) {
                        LocalBroadcastManager.getInstance(MarsrutaiLTApp.getContext()).sendBroadcast(result);
                    }
                });
                
                task.execute(taskCriteriaProvider.getCriteria());
            }
        };
        timer = new Timer();
        timer.schedule(timerTask, 500, TASK_EXECUTION_PERIOD);
    }

    public View setUpControls(ViewGroup root) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        
        FrameLayout frameLayout = new FrameLayout(context);
        View view = inflater.inflate(R.layout.button_vehicle_position, frameLayout);
        
        vehiclePositionToggler = (ToggleButton) view.findViewById(R.id.vehicle_position_toggler);
        vehiclePositionToggler.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    start();
                }
                else {
                    pause();
                    clear();
                }
            }
        });
        vehiclePositionToggler.setChecked(true);
        
        return frameLayout;
    }
    
    public CustomInfoWindowAdapter getInfoWindowAdapter() {
        return new VehiclePositionInfoWindowAdapter(context);
    }
    
    public GetVehiclePositionsTaskCriteriaProvider getTaskCriteriaProvider() {
        return taskCriteriaProvider;
    }

    public void setTaskCriteriaProvider(GetVehiclePositionsTaskCriteriaProvider taskCriteriaProvider) {
        this.taskCriteriaProvider = taskCriteriaProvider;
    }

    public class VehiclePositionInfoWindowAdapter implements CustomInfoWindowAdapter {
        
        private final View contents;
        
        private TextView transportId;

        public VehiclePositionInfoWindowAdapter(Context context) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            contents = inflater.inflate(R.layout.route_node_info, null);
            transportId = (TextView) contents.findViewById(R.id.transportId1);
        }

        @Override
        public View getInfoWindow(Marker marker) {
            return null;
        }

        @Override
        public View getInfoContents(Marker marker) {
            render(marker, contents);
            return contents;
        }
        
        public boolean isSupported(Marker marker) {
            return vehiclePositionMarkers.containsKey(marker);
        }

        private void render(Marker marker, View view) {
            String title = marker.getTitle();
            TextView titleUi = ((TextView) view.findViewById(R.id.title));
            titleUi.setText(title);

            String snippet = marker.getSnippet();
            TextView snippetUi = ((TextView) view.findViewById(R.id.snippet));
            snippetUi.setText(snippet);

            // unused
            contents.findViewById(R.id.transportId2).setVisibility(View.GONE);
            contents.findViewById(R.id.transportIdSeparator).setVisibility(View.GONE);
            
            VehiclePosition vehiclePosition = vehiclePositionMarkers.get(marker);
            fillViewInfo(transportId, vehiclePosition.getScheduleName(), vehiclePosition.getTransportId(), vehiclePosition.getScopeFolder());
        }

        protected void fillViewInfo(TextView view, String scheduleName, String transportId, String scopeFolder) {
            if (scheduleName == null) {
                view.setVisibility(View.GONE);
                return;
            }
            
            view.setVisibility(View.VISIBLE);
            view.setText(scheduleName);
            
            TransportInfo info = AppContext.getTransportInfoResolver().getTransportInfo(scopeFolder, transportId);
            if (info != null) {
                view.setBackgroundColor(info.getColor());
            }
        }
    }
    
    /**
     * Provides criteria for {@link GetVehiclePositionsTask#execute(GetVehiclePositionsTask.Criteria...)}
     * 
     * @author Justas
     */
    public static interface GetVehiclePositionsTaskCriteriaProvider {
        
        GetVehiclePositionsTask.Criteria[] getCriteria();
    }
}
